#ifndef __NET_PACKET_H__
#define __NET_PACKET_H__

#include <memory>
#include <atomic>

namespace LiteNetLib
{
    extern std::atomic<int32_t> nowPackCount;

    enum class PacketProperty
    {
        Unreliable,
        Channeled,
        Ack,
        Ping,
        Pong,
        ConnectRequest,
        ConnectAccept,
        Disconnect,
        UnconnectedMessage,
        MtuCheck,
        MtuOk,
        Broadcast,
        Merged,
        ShutdownOk,
        PeerNotFound,
        InvalidProtocol,
        NatMessage,
        Empty
    };

    class NetPacket
    {
    private:
        char *m_rawData;
        // m_RawData里面有多少空间可用
        uint32_t m_rawDataSize;
        // 这个包里面的数据大小
        uint32_t m_packetSize;

    public:
        NetPacket(uint32_t packetSize);
        NetPacket(PacketProperty property, uint32_t packetSize);
        ~NetPacket();

        /***********************************************************************************************************
         * @brief 重新设置缓存的大小
         * @param newRawDataSize 新的缓存的大小，小于原来的大小将不会生效
        ***********************************************************************************************************/
        void expansionRawDataSize(uint32_t newRawDataSize);

        /***********************************************************************************************************
         * @brief 设置包的大小，若包的大小超过缓存的大小的话，会自动调用 expansionRawDataSize 扩展缓存
         * @param newPacketSize 新的包的大小
        ***********************************************************************************************************/
        void resetPacketSize(uint32_t newPacketSize);

        // ======================== 查询和设置包头 ========================
        PacketProperty getProperty();
        void setProperty(PacketProperty value);

        // ======================== 获取和设置连接编号 =====================
        uint8_t getConnectionNumber();
        void setConnectionNumber(uint8_t value);

        // ======================== 获取和设置序列 ========================
        uint16_t getSequence();
        void setSequence(uint16_t value);

        // ======================== 是否碎片化的 ==========================
        bool isFragmented();
        void markFragmented();

        // ======================== 获取和设置通道ID ======================
        uint8_t getChannelId();
        void setChannelId(uint8_t value);

        // ======================== 获取和设置片段ID ======================
        uint16_t getFragmentId();
        void setFragmentId(uint16_t value);


        // ======================== 获取和设置片段部分 =====================
        uint16_t getFragmentPart();
        void setFragmentPart(uint16_t value);

        // ======================== 获取片段总数 ==========================
        uint16_t getFragmentsTotal();
        void setFragmentsTotal(uint16_t value);

        inline uint32_t getHeaderSize();
        inline char *getRawData();
        inline uint32_t getRawDataSize();
        inline uint32_t getPacketSize();

        bool verify();

        static uint32_t GetHeaderSize(PacketProperty type);


        // ======================== 查询和设置包头 ========================
        static PacketProperty GetProperty(const char *m_rawData);
        static void SetProperty(char *m_rawData, PacketProperty value);

        // ======================== 获取和设置连接编号 =====================
        static uint8_t GetConnectionNumber(const char* m_rawData);
        static void SetConnectionNumber(char *m_rawData, uint8_t value);

        // ======================== 获取和设置序列 ========================
        static uint16_t GetSequence(const char* m_rawData);
        static void SetSequence(char* m_rawData, uint16_t value);

        // ======================== 是否碎片化的 ==========================
        static bool IsFragmented(const char *m_rawData);
        static void MarkFragmented(char * m_rawData);

        // ======================== 获取和设置通道ID ======================
        static uint8_t GetChannelId(const char *m_rawData);
        static void SetChannelId(char *m_rawData, uint8_t value);

        // ======================== 获取和设置片段ID ======================
        static uint16_t GetFragmentId(const char* m_rawData);
        static void SetFragmentId(char *m_rawData, uint16_t value);

        // ======================== 获取和设置片段部分 =====================
        static uint16_t GetFragmentPart(const char* m_rawData);
        static void SetFragmentPart(char *m_rawData, uint16_t value);

        // ======================== 获取片段总数 ==========================
        static uint16_t GetFragmentsTotal(const char* m_rawData);
        static void SetFragmentsTotal(char *m_rawData, uint16_t value);

        static bool Verify(const char *theData, uint32_t dataSize);

    };

    inline char *NetPacket::getRawData()
    {
        return m_rawData;
    }
    inline uint32_t NetPacket::getRawDataSize()
    {
        return m_rawDataSize;
    }
    inline uint32_t NetPacket::getPacketSize()
    {
        return m_packetSize;
    }
    inline uint32_t NetPacket::getHeaderSize()
    {
        return NetPacket::GetHeaderSize(getProperty());
    }


    struct NetDataReader
    {
        NetPacket *netPacket;
        uint32_t headerSize;

        inline char *getUserData()
        {
            return netPacket->getRawData() + headerSize;
        }

        inline uint32_t getUserDataSize()
        {
            if (headerSize > netPacket->getPacketSize())
            {
                return 0;
            }
            return netPacket->getPacketSize() - headerSize;
        }
    };

}

#endif